﻿using System;
using System.IO;
using System.Text;
using System.Collections;
using ICSharpCode.SharpZipLib.Zip;
using UnityEngine;

/**
 *  @2015/07/23 增加解压进度条的支持，使用coroutine实现，不涉及线程问题
 */ 
public class UnCompress {

    #region private property

    // 每帧处理的entry数量, 数量越大，每帧处理的entry越多
    private int                     ENTRIES_PER_FRAME = 16;

    // 压缩进度 
    private long                    curValue = 0;
    private long                    totalValue = 1;

    // file stream
    private Stream                  sourceArchiveFile;
    
    // zip stream
    private ZipInputStream          zipStream;

    // 目标路径
    private string                  destinationDirectoryName;
    private byte[]                  buffer = new byte[1024 * 1024];

    // 输出流
    private Stream                  writeFileStream;
    
    // 当前状态
    private State                   curState = State.Uncompleted;

    #endregion //private property

    #region public property

    public enum State
    {
        Uncompleted,
        Succeed,
        Failed,
    }


    public State                    CurState
    {
        get { return curState;}
        set { curState = value;}
    }

    /**
     * 压缩进度 [0-1] 
     */
    public float                    progress
    {
        get {
            if (curState != State.Uncompleted) {
                return 0;
            }
            else {
                return (float)curValue / totalValue;
            }        
        }
    }
    #endregion  //public property

    #region Private Interface
    /** 
     *  return
     *      true  : process finished
     *      false : process not finshed
     */ 
    bool Process()
    {
        try
        {
            // 处理下一个entry
            if (writeFileStream == null)
            {
                ProcessNextEntry();

                if (curState != State.Uncompleted)
                    return true;
            }
            else
            {
                ProcessFile();
            }
        }
        catch(Exception e)
        {
            curState = State.Failed;
            Debug.LogException(e);
            return true;
        }

        return false;
    }

    /**
     *  处理当前文件
     *      读zip
     *      写文件
     */
    void ProcessFile()
    {
        var size = zipStream.Read(buffer, 0, buffer.Length);

        if (size <= 0)
        {
            writeFileStream.Close();
            writeFileStream = null;
        }
        else
        {
            curValue = sourceArchiveFile.Position;
            totalValue = sourceArchiveFile.Length;
            writeFileStream.Write(buffer, 0, size);
        }
    }

    void ProcessNextEntry()
    {
        var theEntry = zipStream.GetNextEntry();

        // 最后一个entry
        if (theEntry == null)
        {
            curState = State.Succeed;
        }
        else
        {
            // 1. 创建目标文件夹            
            string fileName = Path.GetFileName(theEntry.Name);
            if (string.IsNullOrEmpty(fileName))
            {
                return;
            }
            string directoryName = Path.GetDirectoryName(theEntry.Name);
            if (!string.IsNullOrEmpty(directoryName))
            {
                string path = Path.Combine(destinationDirectoryName, directoryName);
                Directory.CreateDirectory(path);
            }
                
            // 2. 打开文件(写)
            string destPath = Path.Combine(destinationDirectoryName, theEntry.Name);
            writeFileStream = File.OpenWrite(destPath);
        }            
    }

    #endregion // Private Interface

    #region Public Interface

    /**
     *  sourceArchiveFileName       zip文件夹路径 + 名称
     *  directoryName               压缩的目标文件夹路径
     */
    public UnCompress(string sourceArchiveFileName, string directoryName)
    {
        if (!Directory.Exists(directoryName))
            Directory.CreateDirectory(directoryName);

        sourceArchiveFile = File.OpenRead(sourceArchiveFileName);
        zipStream = new ZipInputStream(sourceArchiveFile);
        destinationDirectoryName = directoryName;
    }

    public void Update()
    {
        for (int i = 0; i < ENTRIES_PER_FRAME; ++i)
        {
            if (Process())
            {
                break;
            }
        }
    }

    /**
     * 释放资源,结束使用需释放
     */ 
    public void Dispatch()
    {
        if (sourceArchiveFile != null)
        {
            sourceArchiveFile.Close();
            sourceArchiveFile = null;
        }

        if (zipStream != null)
        {
            zipStream.Close();
            zipStream = null;
        }

        if (writeFileStream != null)
        {
            writeFileStream.Close();
            writeFileStream = null;
        }

        buffer = null;
    }

    #endregion Public Interface

}
